﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Scripting.Actions;
using Microsoft.Scripting.Runtime;
using Microsoft.Scripting.Actions.Calls;
using Microsoft.Linq.Expressions;
using Microsoft.Scripting.Generation;
using Microsoft.Scripting.Ast;

namespace JavascriptEngine.Runtime
{
    public class JavascriptBinder : DefaultBinder
    {
        public JavascriptBinder(ScriptDomainManager manager) 
            : base(manager)
        {
        }

        public override bool CanConvertFrom(Type fromType, Type toType, bool toNotNullable, NarrowingLevel level)
        {
            if (fromType == typeof(DynamicNull)) return true;
            if (fromType == toType || toType.IsAssignableFrom(fromType)) return true;
            if (CompilerHelpers.HasImplicitConversion(fromType, toType)) return true;

            var fromCode = Type.GetTypeCode(fromType);
            var toCode = Type.GetTypeCode(toType);

            if (fromCode == TypeCode.Int32
                && (toCode == TypeCode.Int64 || toCode == TypeCode.Double || toCode == TypeCode.Decimal))
                return true;

            if (fromCode == TypeCode.Double
                && toCode == TypeCode.Decimal)
                return true;

            return (fromCode == TypeCode.String || toCode == TypeCode.String);
        }

        public override Candidate PreferConvert(Type t1, Type t2)
        {
            var t1Code = Type.GetTypeCode(t1);
            var t2Code = Type.GetTypeCode(t2);

            return (t1Code > t2Code)
                ? Candidate.One
                : (t1Code < t2Code) ? Candidate.Two : Candidate.Equivalent;
        }

        public override Expression ConvertExpression(Expression expr, Type toType, ConversionResultKind kind, Expression context)
        {
            var exprType = expr.Type;

            if (toType == typeof(object))
            {
                return (exprType.IsValueType)
                    ? Utils.Convert(expr, toType)
                    : expr;
            }

            if (toType.IsAssignableFrom(exprType))
            {
                return expr;
            }

            var visType = CompilerHelpers.GetVisibleType(toType);

            return Expression.Dynamic(
                new JavascriptConvertBinder(this, visType, kind),
                visType,
                expr);
        }

        public override object Convert(object obj, Type toType)
        {
            return base.Convert(obj, toType);
        }

    }
}
